/*
 * JANN - a Java toolkit for creating arbitrary Artificial Neural Networks.
 * 
 * Copyright (c) 2009 Matthijs Snel
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.jann;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;


public class Trainer {

	/**
	 * Maps training vectors to target vectors
	 */
	private List<TrainingItem> trainingData;
	/**
	 * Network to be trained
	 */
	private NeuralNet trainee;
	/**
	 * Sum of squared errors
	 */
	private String SSE;
	/**
	 * Error for curr epoch
	 */
	private double currEpochErr;
	private int currItem;
	
	public Trainer() {
		trainingData = new ArrayList<TrainingItem>();
		SSE = "";
		currEpochErr = 0;
		currItem = 0;
	}
	
	public void addTrainingItem( TrainingItem item ) {
		trainingData.add(item);
	}
	
	public void printSSE() {
		System.out.println(SSE);
	}
	
	public void setTrainee( NeuralNet toTrain ) {
		trainee = toTrain;
	}
	
	public void setTrainingData( Collection<? extends TrainingItem> data ) {
		trainingData.clear();
		trainingData.addAll(data);
	}
	
	public void train( int epochs ) {
		SSE = "";
		for ( int i = 0; i < epochs; i++ )
			doEpoch();
	}
	
	public void showNext() {
		trainee.feedforward( trainingData.get(currItem).input );
		System.out.println( Arrays.toString(trainingData.get(currItem).input) + "->" + Arrays.toString(trainee.getOutput()) );
		if ( ++currItem >= trainingData.size() )
			currItem = 0;
	}
	
	protected double[] computeError( double[] o, double t[] ) {
		double delta[] = new double[o.length],
				error = 0;
		for ( int i = 0; i < delta.length; i++ ) {
			delta[i] = o[i] - t[i];
			error += delta[i] * delta[i];
		}
		currEpochErr += error;
		return delta;
	}
	
	public void doEpoch() {
		//Collections.shuffle(trainingData);
		currEpochErr = 0;
		for ( TrainingItem item : trainingData ) {
			trainee.feedforward(item.input);
			trainee.backprop( computeError(trainee.getOutput(), item.target) );
		}
		SSE += currEpochErr + ",";
	}
}
